#include "stdafx.h"

#include <MyControls/SegmentSetRow.h>
#include <MyControls/SegmentRow.h>
#include <MyControls/Xline.h>

#include <math/round.h>
#include <sstream>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace My {

// LeftBracket

CRect SegmentRowControl::LeftBracket::Bounds() const
{
	CSize s = m_Control.m_Left.Size();
	CRect r(0, 0, s.cx, s.cy);
	if (!m_Control.IsEmpty())
	{
		int x = m_Control.TimeToClient(m_Control.GetTimeSpan().first);
		int mx = m_Control.TimeToClient(m_Control.GetOrigin()) - m_Control.m_Mask.Width() / 2; 
		if (x > mx)
			x = mx;
		r.OffsetRect(x - s.cx + 1, 1);
	}
	return r;
}

void SegmentRowControl::LeftBracket::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	Control::HandleLeftDown(flags, p);
	if (!m_Control.IsEmpty())
		m_Control.GetSegments()->SetActiveSegment(m_Control.m_Index);
}

bool SegmentRowControl::LeftBracket::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	if (m_Control.IsEmpty())
		return false;
	if (m_Control.GetSegment().IsProcessed())
		return false;
	m_StartPos = ClientToParent(p0).x;
	m_StartValue = m_Control.TimeToClient(m_Control.GetTimeSpan().first);
	return true;
}

void SegmentRowControl::LeftBracket::HandleDragEnd(unsigned int flags, const CPoint & p)
{
	m_Timeline.GetSequence()->SetPreviewFrame(m_Timeline.GetCurrentFrame());
	m_Control.UpdateTimeSpan();
	m_Timeline.OnAutoScroll(false);
	m_Timeline.SetTimeAutoScroll(0);
}

void SegmentRowControl::LeftBracket::HandleDragging(unsigned int flags, const CPoint & p)
{
	int x = m_StartValue + ClientToParent(p).x - m_StartPos;
	int t = round(m_Control.ClientToTime(x));
	if (t > m_Control.GetOrigin())
		t = m_Control.GetOrigin();
	Span s = m_Control.GetTimeSpan();
	s.first = t;
	m_Control.SetTimeSpan(s);
	m_Timeline.GetSequence()->SetPreviewFrame(m_Control.GetTimeSpan().first);

	// Autoscroll?
	x -= m_StartValue - m_StartPos;
	int gap = m_Control.m_Gap;
	if (x < gap)
		m_Timeline.SetTimeAutoScroll(x - gap);
	else if (x > m_Control.Width() - gap)
		m_Timeline.SetTimeAutoScroll(x - m_Control.Width() + gap);
	else
		m_Timeline.SetTimeAutoScroll(0);
}

// RightBracket

CRect SegmentRowControl::RightBracket::Bounds() const
{
	CSize s = m_Control.m_Right.Size();
	CRect r(0, 0, s.cx, s.cy);
	if (!m_Control.IsEmpty())
	{
		int x = m_Control.TimeToClient(m_Control.GetTimeSpan().last);
		int mx = m_Control.TimeToClient(m_Control.GetOrigin()) - m_Control.m_Mask.Width() / 2 + m_Control.m_Mask.Width(); 
		if (x < mx)
			x = mx;
		r.OffsetRect(x, 1);
	}
	return r;
}

void SegmentRowControl::RightBracket::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	Control::HandleLeftDown(flags, p);
	if (!m_Control.IsEmpty())
		m_Control.GetSegments()->SetActiveSegment(m_Control.m_Index);
}

bool SegmentRowControl::RightBracket::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	if (m_Control.IsEmpty())
		return false;
	if (m_Control.GetSegment().IsProcessed())
		return false;
	m_StartPos = ClientToParent(p0).x;
	m_StartValue = m_Control.TimeToClient(m_Control.GetTimeSpan().last);
	return true;
}

void SegmentRowControl::RightBracket::HandleDragEnd(unsigned int flags, const CPoint & p)
{
	m_Timeline.GetSequence()->SetPreviewFrame(m_Timeline.GetCurrentFrame());
	m_Control.UpdateTimeSpan();
	m_Timeline.OnAutoScroll(false);
	m_Timeline.SetTimeAutoScroll(0);
}

void SegmentRowControl::RightBracket::HandleDragging(unsigned int flags, const CPoint & p)
{
	int x = m_StartValue + ClientToParent(p).x - m_StartPos;
	int t = round(m_Control.ClientToTime(x));
	if (t < m_Control.GetOrigin() + 1)
		t = m_Control.GetOrigin() + 1;
	Span s = m_Control.GetTimeSpan();
	s.last = t;
	m_Control.SetTimeSpan(s);
	m_Timeline.GetSequence()->SetPreviewFrame(m_Control.GetTimeSpan().last);

	// Autoscroll?
	x -= m_StartValue - m_StartPos;
	int gap = m_Control.m_Gap;
	if (x < gap)
		m_Timeline.SetTimeAutoScroll(x - gap);
	else if (x > m_Control.Width() - gap)
		m_Timeline.SetTimeAutoScroll(x - m_Control.Width() + gap);
	else
		m_Timeline.SetTimeAutoScroll(0);
}

// SegmentRowControl

SegmentRowControl::SegmentRowControl(SegmentSetRow & r, int index) : 
	m_SegmentSetRow(r),
	m_Timeline(r.GetXline()),
	m_LeftBracket(* this),
	m_RightBracket(* this)
{
	m_Index = index;

	assert(m_SegmentSetRow.GetSegments() != 0);
	m_pSegment = &m_SegmentSetRow.GetSegments()->GetSegment(m_Index);
	m_Gap = 15;

	m_Left.Load("SegmentLeft", "SegmentLeftMask");
	m_Right.Load("SegmentRight", "SegmentRightMask");
	m_Mask.Load("Mask");
	m_MaskPressed.Load("MaskPressed");
	m_MaskGrayed.Load("MaskGrayed");

	VERIFY (m_DarkOrangeRipple.LoadBitmap("DarkOrangeRipple"));
	VERIFY (m_LightOrangeRipple.LoadBitmap("LightOrangeRipple"));
	VERIFY (m_DarkBlueRipple.LoadBitmap("DarkBlueRipple"));
	VERIFY (m_LightBlueRipple.LoadBitmap("LightBlueRipple"));

	GetSegment().Attach(this);
	SetTimeSpan(GetSegment().GetTimeSpan());
}

SegmentRowControl::~SegmentRowControl()
{
	m_pSegment->Detach(this);
}

String SegmentRowControl::Hint() const
{
	std::ostringstream out;
	out << "Segment";
	if (!IsEmpty()) {
		Span s = GetSegment().GetTimeSpan();
		out << " [" << s.first << "-" << s.last << "]";
	}
	return out.str();
}

Segment & SegmentRowControl::GetSegment() const
{
	return * m_pSegment;
}

bool SegmentRowControl::IsEmpty() const
{
	return m_SegmentSetRow.GetSegments() == 0;
}

CRect SegmentRowControl::Bounds() const
{
	if (IsEmpty() || Parent() == 0)
		return CRect(0, 0, 0, 0);
	CRect r = Parent()->ClientRect();
	Span s = m_SegmentSetRow.GetXline().GetTimeCol().GetBounds();
	r.left = s.first;
	r.right = s.last;
	return r;
}

void SegmentRowControl::ProcessEvent(My::Event & e)
{
	if (e.Is<Segment::TimeSpanChange>())
		OnTimeSpanChanged();
	else if (e.Is<Segment::DataSpanChange>())
		OnDataSpanChanged();
	else if (e.Is<Segment::StateChange>())
		OnStatusChanged();
	else
		Container::ProcessEvent(e);
}

void SegmentRowControl::Paint(CDC * pDC, const CRect & rect)
{
	if (IsEmpty())
		return;

	CRect rClient = ClientRect();

	ClipRect clip(this, pDC, rClient);

	CBrush bFace; bFace.CreateSysColorBrush(COLOR_BTNFACE);
	CBrush bBlack(RGB(0, 0, 0));
	CBrush bLight; bLight.CreateSysColorBrush(COLOR_BTNHILIGHT);
	CBrush bHalfLight(MeanSysColor(COLOR_BTNFACE, COLOR_BTNHILIGHT));
	CBrush bHalfShadow(MeanSysColor(COLOR_BTNFACE, COLOR_BTNSHADOW));
	CBrush bShadow; bShadow.CreateSysColorBrush(COLOR_BTNSHADOW);

	Span s = GetSegment().GetDataSpan();
	CRect rDataSpan;
	rDataSpan.top = rClient.top;
	rDataSpan.left = TimeToClient(s.first); 
	rDataSpan.right = TimeToClient(s.last) + 1;
	rDataSpan.bottom = rClient.bottom - 1;

	pDC->FillRect(&rDataSpan, &bHalfLight);
	pDC->FrameRect(&rDataSpan, &bShadow);

	s = GetTimeSpan();

	CRect rTimeSpan;
	rTimeSpan.top = rClient.top;
	rTimeSpan.left = TimeToClient(s.first); 
	rTimeSpan.right = TimeToClient(s.last) + 1;
	rTimeSpan.bottom = rClient.bottom - 1;

	CBrush bGround;
	CBrush bData;

	if (!GetSegment().IsProcessed()) {

		if (GetSegments()->GetActiveSegment() == m_Index) {
			bGround.CreatePatternBrush(&m_LightOrangeRipple);
			bData.CreatePatternBrush(&m_DarkOrangeRipple);
		} else {
			bGround.CreateSolidBrush(RGB(255, 240, 192));
			bData.CreateSolidBrush(RGB(255, 192, 0));
		}

	} else {

		if (GetSegments()->GetActiveSegment() == m_Index) {
			bGround.CreatePatternBrush(&m_LightBlueRipple);
			bData.CreatePatternBrush(&m_DarkBlueRipple);
		} else {
			bGround.CreateSolidBrush(RGB(224, 224, 255));
			bData.CreateSolidBrush(RGB(192, 192, 255));
		}

	}

	pDC->SetBrushOrg(GetWindowOffset() + CPoint(TimeToClient(GetOrigin()), 0) + m_BrushOrg);

	pDC->FillRect(&rTimeSpan, &bGround);
	rDataSpan.IntersectRect(&rDataSpan, &rTimeSpan);
	pDC->FillRect(&rDataSpan, &bData);

	CBrush bFrame;
//	if (GetSegments()->GetActiveSegment() == m_Index && HasFocus())
//		bFrame.CreatePatternBrush(&m_Alternate);
//	else
		bFrame.CreateSysColorBrush(COLOR_BTNSHADOW);

	pDC->FrameRect(&rTimeSpan, &bFrame);

	pDC->SetBrushOrg(0, 0);

	CRect rMask(rClient);
	rMask.left = TimeToClient(GetOrigin()) - m_Mask.Width() / 2;
	rMask.right = rMask.left + m_Mask.Width();

	if (GetSegment().IsProcessed())
		m_MaskGrayed.Draw(pDC, rMask.left, 0);
	else {
		if (GetSegment().IsMaskEditing())
			m_MaskPressed.Draw(pDC, rMask.left, 0);
		else
			m_Mask.Draw(pDC, rMask.left, 0);
	}

	if (rTimeSpan.left > rMask.left)
		rTimeSpan.left = rMask.left;
	if (rTimeSpan.right < rMask.right)
		rTimeSpan.right = rMask.right;

	m_Left.Draw(pDC, rTimeSpan.left - m_Left.Width() + 1, 0);
	m_Right.Draw(pDC, rTimeSpan.right - 1, 0);
	
//	pDC->SelectClipRgn(0);
}

void SegmentRowControl::HandleKeyDown(unsigned int key, unsigned int count, unsigned int flags)
{
	if (IsEmpty())
		return;
	if (key == VK_RETURN) {
		GetSegment().StartProcessing();
		return;
	}
	if (key == VK_ESCAPE) {
		GetSegment().CancelProcessing(false);
		return;
	}
}

void SegmentRowControl::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	My::Control::HandleLeftDown(flags, p);
	if (IsEmpty())
		return;
	CRect rSpan = GetUnionSpanRect();
	if (rSpan.PtInRect(p)) {
		CRect rMask(rSpan);
		rMask.left = TimeToClient(GetOrigin()) - m_Mask.Width() / 2;
		rMask.right = rMask.left + m_Mask.Width();
		if (rMask.PtInRect(p) && !GetSegment().IsProcessed()) {
			if (GetSegment().IsMaskEditing())
				GetSegment().CloseMaskEditor();
			else {
				GetSegments()->SetActiveSegment(m_Index);
				m_Timeline.GetSequence()->SetCurrentFrame(GetOrigin());
				GetSegment().OpenMaskEditor();
			}
		} else {
			GetSegments()->SetActiveSegment(m_Index);
		}
	} else {
		GetSegments()->SetActiveSegment(-1);
	}
}

void SegmentRowControl::HandleLeftDoubleClick(unsigned int flags, const CPoint &p)
{
	My::Control::HandleLeftDoubleClick(flags, p);
	if (IsEmpty())
		return;
	if (GetUnionSpanRect().PtInRect(p)) {
		if (GetSegment().IsProcessed())
			GetSegment().CancelProcessing(false);
		else
			GetSegment().StartProcessing();
	}
}

bool SegmentRowControl::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
/*	if (IsEmpty())
		return false;
	Span d = GetSegment().GetDataSpan();
	Span s = GetTimeSpan();
	if (s.first > d.first)
		s.first = d.first;
	if (s.last < d.last)
		s.last = d.last;
	CRect rSpan(ClientRect());
	rSpan.left = TimeToClient(s.first) - m_Left.Width(); 
	rSpan.right = TimeToClient(s.last) + m_Right.Width();
	return rSpan.PtInRect(p) != 0;
*/
	return false;
}

void SegmentRowControl::HandleDragEnd(unsigned int flags, const CPoint & p)
{
/*	HideInsertion();
	m_ShowInsertion = false;
	m_InsertionSource = -1;

	int y = ClientToParent(p).y;
	int pos = round(double(y - RowHeight - DividerHeight - 2 +  GetVOffset()) / RowHeight);
	if (pos < 0)
		pos = 0;
	if (pos > int(GetSegments()->GetSegmentCount()))
		pos = GetSegments()->GetSegmentCount();

	GetSegments()->MoveSelection(pos);
	OnAutoScroll(false);
	SetVAutoScroll(0);
*/
}

void SegmentRowControl::HandleDragging(unsigned int flags, const CPoint & p)
{
/*	m_ShowInsertion = true;
	m_InsertionSource = m_Index;
	ShowInsertion();

	int y = ClientToParent(p).y;
	int pos = round(double(y - RowHeight - DividerHeight - 2 +  GetVOffset()) / RowHeight);
	if (pos < 0)
		pos = 0;
	if (pos > int(GetSegments()->GetSegmentCount()))
		pos = GetSegments()->GetSegmentCount();

	Insertion(pos);

	// Autoscroll?
	if (y < RowHeight + DividerHeight + m_Gap)
		SetVAutoScroll(y - RowHeight - DividerHeight - m_Gap);
	else if (y > Height() - m_Gap)
		SetVAutoScroll(y - Height() + m_Gap);
	else
		SetVAutoScroll(0);
*/
}

void SegmentRowControl::HandleFocus()
{
	Invalidate();
}

void SegmentRowControl::HandleUnfocus()
{
	Invalidate();
}

Span SegmentRowControl::GetTimeSpan() const
{
	return m_TimeSpan;
}

void SegmentRowControl::SetTimeSpan(Span s)
{
	s.Intersect(m_Timeline.GetSequence()->GetTimeSpan());
	if (s != m_TimeSpan) {
		m_TimeSpan = s;
		Invalidate();
	}
}

void SegmentRowControl::UpdateTimeSpan()
{
	if (!IsEmpty())
		GetSegment().SetTimeSpan(GetTimeSpan());
}

int SegmentRowControl::GetOrigin() const
{
	return IsEmpty() ? 0 : GetSegment().GetOrigin();
}

CRect SegmentRowControl::GetUnionSpanRect() const
{
	Span s = GetSegment().GetDataSpan().Unite(GetTimeSpan());
	CRect rSpan(ClientRect());
	rSpan.left = TimeToClient(s.first) - m_Left.Width(); 
	rSpan.right = TimeToClient(s.last) + m_Right.Width();
	return rSpan;
}

void SegmentRowControl::OnInitialUpdate()
{
	SetTimeSpan(GetSegment().GetTimeSpan());
}

void SegmentRowControl::OnTimeSpanChanged()
{
	if (!IsEmpty())
		SetTimeSpan(GetSegment().GetTimeSpan());
}

void SegmentRowControl::OnDataSpanChanged()
{
	Invalidate();
}

void SegmentRowControl::OnStatusChanged()
{
	Invalidate();
}

int SegmentRowControl::TimeToClient(double time) const
{
	int w = Width() - 2 * m_Gap;
	return m_Gap + round(w * (time - m_Timeline.GetTimeOffset()) / m_Timeline.GetTimePage());
}

double SegmentRowControl::ClientToTime(int x) const
{
	int w = Width() - 2 * m_Gap;
	return m_Timeline.GetTimeOffset() + m_Timeline.GetTimePage() * double(x - m_Gap) / w;
}

SegmentSet * SegmentRowControl::GetSegments() const
{
	return m_SegmentSetRow.GetSegments();
}

// SegmentRow

SegmentRow::SegmentRow(SegmentSetRow & r, int index) :
	m_Owner(r),
	m_Control(r, index)
{
	Add(&m_Control);
	m_Control.GetSegment().Attach(this);
	m_Owner.GetSegments()->GetDoc().Attach(this);
	SetRowHeight(14);
}

SegmentRow::~SegmentRow()
{
	m_Owner.GetSegments()->GetDoc().Detach(this);
	m_Control.GetSegment().Detach(this);
}

String SegmentRow::GetCaption() const
{
	CString s;
	s.Format("<%02d-[%02d]-%02d>",
		m_Control.GetSegment().GetTimeSpan().first,
		m_Control.GetSegment().GetOrigin(),
		m_Control.GetSegment().GetTimeSpan().last);
	return String(LPCTSTR(s));
}

void SegmentRow::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	TreeRow::HandleLeftDown(flags, p);
	CRect r(ClientRect());
	Span s = m_Owner.GetXline().GetNameCol().GetBounds();
	r.left = s.first;
	r.right = s.last;
	if (r.PtInRect(p))
		m_Owner.GetSegments()->SetActiveSegment(m_Control.GetIndex());
}

void SegmentRow::HandlePaint(CDC * pDC, const CRect & rClip)
{
	GridRow::HandlePaint(pDC, rClip);
	CRect rClient = ClientRect();
	CPen pen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT));
	pDC->SelectObject(&pen);
	pDC->MoveTo(rClient.left, rClient.bottom - 1);
	pDC->LineTo(rClient.right, rClient.bottom - 1);
}

void SegmentRow::PaintCell(CDC * pDC, const CRect & rClip, const Column & col, const CRect & rCell)
{
	if (&col == &m_Owner.GetXline().GetNameCol()) {
		if (&m_Control.GetSegment() == m_Owner.GetSegments()->GetActiveSegmentPtr()) {
			CRect r(rCell);
			r.right--;
			r.bottom--;
			CBrush bViolet(RGB(224, 224, 255));
			pDC->FillRect(&r, &bViolet);
		}
		TreeRow::PaintCaption(pDC, rCell);
	}
}

void SegmentRow::ProcessEvent(Event & e)
{
	if (e.Is<Segment::Event>()) {
		Invalidate();
	} 
	else if (e.Is<Doc::SelectionChange>()) {
		Invalidate();
	}
	GridRow::ProcessEvent(e);
}

} // My